package nachos.threads;

import java.util.Comparator;
import java.util.PriorityQueue;

import nachos.machine.*;

/**
 * Uses the hardware timer to provide preemption, and to allow threads to sleep
 * until a certain time.
 */
public class Alarm
{
	/**
	 * Allocate a new Alarm. Set the machine's timer interrupt handler to this
	 * alarm's callback.
	 * 
	 * <p>
	 * <b>Note</b>: Nachos will not function correctly with more than one alarm.
	 */
	public Alarm()
	{
		Machine.timer().setInterruptHandler(new Runnable()
		{
			public void run()
			{
				timerInterrupt();
			}
		});
		waitQueue = new PriorityQueue<KThread>(11,new WaitComparator());
	}

	/**
	 * The timer interrupt handler. This is called by the machine's timer
	 * periodically (approximately every 500 clock ticks). Causes the current
	 * thread to yield, forcing a context switch if there is another thread that
	 * should be run.
	 */
	//TODO: FIX THIS!
	public void timerInterrupt()
	{
		while(!waitQueue.isEmpty() && waitQueue.peek().getWakeTime() <= Machine.timer().getTime())
		{
			waitQueue.poll().ready();
			//System.out.println("Waking at " + Machine.timer().getTime());
		}
	}

	/**
	 * Put the current thread to sleep for at least <i>x</i> ticks, waking it up
	 * in the timer interrupt handler. The thread must be woken up (placed in
	 * the scheduler ready set) during the first timer interrupt where
	 * 
	 * <p>
	 * <blockquote> (current time) >= (WaitUntil called time)+(x) </blockquote>
	 * 
	 * @param x
	 *            the minimum number of clock ticks to wait.
	 * 
	 * @see nachos.machine.Timer#getTime()
	 */
	public void waitUntil(long x)
	{
		boolean intStatus = Machine.interrupt().disable();

		//System.out.println("Sleeping at " + Machine.timer().getTime());
		long wakeTime = Machine.timer().getTime() + x;
		KThread current = KThread.currentThread();
		current.setWakeTime(wakeTime);
		// Add to the wait queue, then sleep
		waitQueue.add(current);
		current.sleep();

		Machine.interrupt().restore(intStatus);
	}

	/**
	 * Inner class to compare two KThreads. Used in the waitQueue.
	 */
	private static class WaitComparator implements Comparator<KThread>
	{

		public int compare(KThread o1, KThread o2)
		{
			long result = o1.getWakeTime() - o2.getWakeTime();
			if (result < 0)
				return -1;
			else if (result == 0)
				return 0;
			else
				return 1;
		}
	}
	
	private PriorityQueue<KThread> waitQueue;
}
